import com.fasterxml.jackson.databind.ObjectMapper;
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import java.awt.*;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

class Message{
    public String from;
    public String to;
    public String message;  //这几个名字具体要怎么叫 要和前面约定好的格式(前端的)是一样的 (和 json 中的 属性名字要保持一致)

    // 为了方便打印,可以加上 toString 方法

    @Override
    public String toString() {
        return "Message{" +
                "from='" + from + '\'' +
                ", to='" + to + '\'' +
                ", message='" + message + '\'' +
                '}';
    }
}

// 一共有三个交互
// 第一个交互 是 前端向服务器 GET/ message.html 获取页面
// 服务器返回一个 message.html

// 第二个交互是
// 页面加载 前端并向服务器 请求一个 Get/message
// 服务器返回一个 [{json 格式的响应}] 把 messageList里的数据返回给浏览器  (需要代码)
// 前端把返回的数据构成 html 片段 显示到 页面上

// 第三个交互
// 点击按钮 POST/message {json 格式内容}  到服务器
// 服务器 把这个数据保存到 messageList中 并 返回一个 ok   (需要代码)
@WebServlet("/message")
public class MessageServlet extends HttpServlet {
    // 这里面有一个要求, 就是 前端写 ajax 请求的时候 url 路径写作 " message " , 不带 / , 此时是一个相对路径的写法
    // 后端处理 ajax 请求 url 路径 写作 "/message" , 前面要带 / , 此时是 Servlet 的 写法 , 而且两者的路径名字要一样

    private ObjectMapper objectMapper = new ObjectMapper();

   // private List<Message> messageList = new ArrayList<>();  // 既然要引入数据库 那么这个东西也就不需要了

    private DataSource dataSource = new MysqlDataSource();

    @Override
    public void init() throws ServletException {
        ((MysqlDataSource) dataSource).setUrl("jdbc:mysql:/127.0.0.1:3306/message_wall?characterEncoding=utf8&&useSSL=false");
        ((MysqlDataSource) dataSource).setUser("root");
        ((MysqlDataSource) dataSource).setPassword("Zuochuyao1314.");
    }

    // (点击提交按钮时发送的请求并处理)
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 读取前端发来的数据, 并把数据保存到服务器这边
        // 这里需要使用 js 来读取到前端的数据,并进行解析
        // 以来已经引进好了

        Message message = objectMapper.readValue(req.getInputStream(),Message.class);
        System.out.println("请求中收到的 message :" + message);
        // 最简单粗暴的方式 是在内存中保存, 可以使用一个集合类, 把所有收到的 message 都存到内存中.

        //messageList.add(message); // 对应的这一行代码肯定也不能再有
        // TODO 要换成插入数据库
        try {
            save(message); // 换成对应的方法
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }


        // 很明显, 保存内存 并不是一个合理的办法, 后来一旦重启服务器, 数据丢失了
        // 相比之下, 把这个数据持久化存储到数据库中, 更科学地

        // 上面是保存到内存当中的方法, 比较容易消失
        // 接下来考虑如何把消息数据存储到 数据库当中
        // 并把数据库引入到代码当中
        // (操作数据库的前提是 把 上面的代码流程都跑通, 接下来就可以操作数据库了)

        // 步骤:
        // 1) 引入数据库的依赖 (在 maven 中央仓库中)
        // 2) 建库建表 (这个需要用到 sql, 都可以写到一个文件中. 后续如果需要把表什么的 往其他机器上迁移, 这样见表的话会比较方便)
        // 3) 编写数据库代码 (使用 jdbc 来编写完成)
        // 返回一个响应
        resp.setStatus(200);
       /* resp.setContentType("application/json");
        resp.getWriter().write("{ok : true}");*/
        // 为了和请求对应上, 服务器返回的数据 也会按照 json 格式来组织

        resp.getWriter().write("ok");
        // 这里面其实怎么返回都行, 返回一个字符串 ok 也是可以的

        // 这一步是 对提交按钮进行响应 并返回一个 ok 到前端, 前端还有对应的 代码进行解析响应
        // 这个就是一个交互流程之一

        // 这是第二步
        // 第三步是继续返回到前端代码, 来处理服务器返回的响应


        // 接下来是第二大方面
        // 当前已经把 数据提交到 服务器保存了
        // 目前还需要能够把服务器的数据拿回到客户端页面上, 并显示
        // 为什么还要从服务器拿消息?
        // 1. 当前页面上显示的数据, 也是在浏览器内存中保存的, 刷新页面/ 关闭了 重新打开, 之前的数据就没有了
        // 2. 其他的客户端打开页面也是有数据的

        // 在页面加载的时候发起Get请求
        // 有前端处理
    }

    //(页面加载是发送的请求并处理)
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 第二步, 服务器收到这个请求, 要处理这个请求并生成响应
        // 通过这个方法来处理当前获取消息列表的 get 请求. 不需要解析参数, 直接返回数据即可
        resp.setStatus(200);
        resp.setContentType("application/json;charset=utf8");
        // TODO 从数据库查询
        List<Message> messageList = null;  // 换成对应的方法
        try {
            messageList = load();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
        String respJson = objectMapper.writeValueAsString(messageList); //对应的这里也不能有了 , 要换成从数据库查询

                // 这里面的数据 是上面服务器收到的每一条消息,都转成了 Message 对象, 放到杉树 List 中了.
                // 返回结果, 也就是这个 List的数据
                // 需要把 List 里的每一个 Message 取出来, 转成 json 字符串, 最终拼到一起, 得到了响应结果  , 但这是一个直观的想法
                // 实际上 , 实际情况要比我们设想的要简单很多
                // jackson 本身就支持 List 类型的数据转成json 数组
        resp.getWriter().write(respJson);
        // 以上这几个代码 就搞定了

        // 这一步是  把 messageList 返回给浏览器 , 并有前端的代码进行对应的响应
        // 这个就是交互流程之一

        // 要主义的是 确保这几个代码的执行顺序
        // setStatus 和 setContentType 必须放在 getWriter 前面
        // 否则可能不会生效(构造出一个非法的 http 响应报文)
        // 这个事情可以认为是一个 Servlet bug
    }

    // 接下来进行到第三大部分
    // 客户端收到响应, 就炫耀针对响应数据进行解析处理
    // 把响应中的信息, 构造成页面元素(html 片段), 并显示出来
    // (这个是最复杂的代码, 需要拼接出 html 片段)

    private void save(Message message) throws SQLException {
        // 通过这个方法把 message  插入到数据库中

        // 第一步 创建数据源 (放到上面)

       /* ((MysqlDataSource) dataSource).setUrl("jdbc:mysql:/127.0.0.1:3306/message_wall?characterEncoding=utf8&&useSSL=false");
        ((MysqlDataSource) dataSource).setUser("root");
        ((MysqlDataSource) dataSource).setPassword("Zuochuyao1314.");*/

        // 第二步 建立连接
        Connection connection = dataSource.getConnection();

        // 第三步 构造SQL
        String sql = "insert into message values(?,?,?)"; // 用 ? 作为占位符
        PreparedStatement statement = connection.prepareStatement(sql);
        statement.setString(1,message.from);
        statement.setString(2,message.to);
        statement.setString(3,message.message);

        // 第四步 执行sql
        statement.executeUpdate();

        // 第五步 回收资源
        statement.close();
        connection.close();
    }

    private List<Message> load() throws SQLException {
        // 通过这个方法从数据库中 读取到 message.

        // 第一步创建数据源  第一步可以单独拎出来

        // 第二步 建立连接
        Connection connection = dataSource.getConnection();

        // 第三步 构造sql
        String sql = "select * from message";
        PreparedStatement statement = connection.prepareStatement(sql);

        // 第四步 执行sql
        ResultSet resultSet = statement.executeQuery();

        // 第五步 遍历结果集合
        List<Message> messageList = new ArrayList<>();
        while(resultSet.next()){
            Message message = new Message();
            message.from = resultSet.getString("from");
            message.to = resultSet.getString("to");
            message.message = resultSet.getString("message");
            messageList.add(message);
        }

        // 第六步 回收资源
        resultSet.close();
        statement.close();
        connection.close();

        // 第七步 返回messageList
        return messageList;

    }


}
